/*
 * Copyright (c) 2021 Futurewei Technologies, Inc.
 *
 * clang2mpl is licensed under Mulan PSL v2.
 * You can use this software according to the terms and conditions of the Mulan
 * PSL v2. You may obtain a copy of Mulan PSL v2 at:
 *
 *     http://license.coscl.org.cn/MulanPSL2
 *
 * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY
 * KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO
 * NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. See the
 * Mulan PSL v2 for more details.
 */
#pragma once

#include "clang/Basic/SourceLocation.h"
#include <vector>
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wunknown-warning-option"
#pragma GCC diagnostic ignored "-Wcast-qual"
#pragma GCC diagnostic ignored "-Wignored-qualifiers"
#pragma GCC diagnostic ignored "-Wreturn-type"
#pragma GCC diagnostic ignored "-Woverloaded-virtual"
#pragma GCC diagnostic ignored "-Wunused-variable"
#pragma GCC diagnostic ignored "-Wsign-compare"
#pragma GCC diagnostic ignored "-Wpedantic"
#pragma GCC diagnostic ignored "-Wtype-limits"
#pragma GCC diagnostic ignored "-Wcovered-switch-default"
#pragma GCC diagnostic ignored "-Wsuggest-override"
#include "mir_builder.h"
#include "mir_nodes.h"
#include "mir_symbol.h"
#include "mir_type.h"
#include "mpl_logging.h"
#include "opcode_info.h"
#pragma GCC diagnostic pop

using namespace maple;

namespace clang {
class RecordDecl;
}

class Result {
private:
  // Statements to run before this result.
  std::vector<StmtNode *> StmtsBefore;
  // An expression to be returned.
  BaseNode *Node = nullptr;
  // A unique ID representing the expression.
  int64_t UniqueID = 0;
  // A symbol to be returned.
  MIRSymbol *Sym = nullptr;
  // The base record decl of a member expression.
  clang::RecordDecl *BaseRecord = nullptr;
  // The field to be accessed.
  FieldID Field = 0;
  // The type of the node. Should be set for all expressions.
  MIRType *NodeTy = nullptr;
  TypeAttrs NodeTyAttrs;
  // The type of the dereferenced value. This also indicates that the expression
  // to be returned is dereferenced, for use by the parent.
  MIRType *DerefTy = nullptr;
  TypeAttrs DerefTyAttrs;
  // Statements to run after this result.
  std::vector<StmtNode *> StmtsAfter;
  // Source location for this node.
  clang::SourceLocation Loc;
  // Can the node be dropped if it is not used? This is used by the
  // pre-/post-increment operators.
  bool MayDrop = false;

  // Returns true if this result will expand into multiple statements.
  bool willExpand();

public:
  using iterator = std::vector<StmtNode *>::iterator;
  using const_iterator = std::vector<StmtNode *>::const_iterator;

  Result(clang::SourceLocation Loc) : Loc(Loc) {}
  Result(BaseNode *Node, int64_t UID, clang::SourceLocation Loc,
         MIRType *Ty = nullptr, TypeAttrs Attrs = TypeAttrs(),
         bool MayDrop = false)
      : Node(Node), UniqueID(UID), NodeTy(Ty), NodeTyAttrs(Attrs), Loc(Loc),
        MayDrop(MayDrop) {
    ASSERT(kOpcodeInfo.IsStmt(Node->op) || Ty,
           "Must include a type with expression result");
  }
  Result(BaseNode *Node, int64_t UID, MIRType *Ty, MIRType *DerefTy,
         clang::SourceLocation Loc, TypeAttrs TyAttrs = TypeAttrs(),
         TypeAttrs DerefTyAttrs = TypeAttrs())
      : Node(Node), UniqueID(UID), NodeTy(Ty), DerefTy(DerefTy), Loc(Loc),
        NodeTyAttrs(TyAttrs), DerefTyAttrs(DerefTyAttrs) {}
  Result(MIRSymbol *Sym, MIRType *Ty, clang::SourceLocation Loc,
         TypeAttrs Attrs = TypeAttrs(), bool MayDrop = false)
      : Sym(Sym), NodeTy(Ty), Loc(Loc), NodeTyAttrs(Attrs), MayDrop(MayDrop) {}

  BaseNode *getNode();
  void setNode(BaseNode *Node, int64_t UID, MIRType *Ty = nullptr,
               TypeAttrs TyAttrs = TypeAttrs(), bool MayDrop = false);
  MIRSymbol *getSym();
  void setSym(MIRSymbol *Sym, MIRType *Ty, bool MayDrop = false);
  clang::RecordDecl *getBaseRecordDecl() { return BaseRecord; }
  clang::RecordDecl *setBaseRecordDecl(clang::RecordDecl *Record);
  int64_t getUniqueID() { return UniqueID; }
  FieldID getField() { return Field; }
  void setField(FieldID Id) { Field = Id; }
  BaseNode *getAddr();
  MIRType *getAddrTy();
  TypeAttrs getAddrTyAttrs();
  MIRType *getValueTy();
  TypeAttrs getValueTyAttrs();
  void setValueTy(MIRType *Ty, TypeAttrs Attrs = TypeAttrs(),
                  bool IsDeref = false);
  void setDerefTypes(MIRType *AddrTy, MIRType *DTy,
                     TypeAttrs AddrTyAttrs = TypeAttrs(),
                     TypeAttrs DTyAttrs = TypeAttrs()) {
    NodeTy = AddrTy;
    DerefTy = DTy;
    NodeTyAttrs = AddrTyAttrs;
    DerefTyAttrs = DTyAttrs;
  }
  clang::SourceLocation getLoc() { return Loc; }
  void setResult(Result &Other);

  bool isDeref();
  bool isSym();
  bool mayDrop();

  void appendStmtBefore(StmtNode *Node);
  void prependStmtBefore(StmtNode *Node);
  void appendStmtAfter(StmtNode *Node);
  void appendStmts(Result R);

  iterator beginStmtsBefore() { return StmtsBefore.begin(); }
  iterator endStmtsBefore() { return StmtsBefore.end(); }
  iterator beginStmtsAfter() { return StmtsAfter.begin(); }
  iterator endStmtsAfter() { return StmtsAfter.end(); }
  // Return true if this node is guaranteed to expand into a single, simple
  // node.
  bool isSimple();
  // Is it safe to execute this node speculatively? Div/rem, ireads, and impure
  // ops are all disallowed.
  bool canSpeculate();
};
